/*
 * This file is part of LangMan.
 *
 * LangMan is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * LangMan is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LangMan.  If not, see <http://www.gnu.org/licenses/>.
 */

package langman.business.data;

import javafx.beans.property.ReadOnlyListWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.WritableObjectValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import langman.business.data.model.*;
import langman.util.doping.SingletonBean;

import java.util.*;

import static com.google.common.base.Preconditions.checkNotNull;

@SingletonBean
public class TranslationService {
    private final ObservableList<Language> languageList = FXCollections.observableArrayList();
    private final WritableObjectValue<Language> referenceLanguage = new SimpleObjectProperty<>();
    private final ObservableList<Category> categories = FXCollections.observableArrayList();
    private final ObservableList<LanguageSet> sets = FXCollections.observableArrayList();

    private final Map<Key, ObservableList<Translation>> translationsForKey = new HashMap<>();

    private final Collection<Key> knownKeys = new HashSet<>();

    public void selectSet(LanguageSet languageSet) {
        if (!sets.contains(languageSet)) {
            return;
        }

        languageList.clear();
        referenceLanguage.set(null);
        categories.clear();
        knownKeys.clear();

        initLanguages(languageSet.getLanguages());


        List<Category> newCategories = languageSet.getCategories();
        categories.setAll(newCategories);
        for (Category newCategory : newCategories) {
            knownKeys.addAll(newCategory.getKeysUnmodifiable().values());
        }
    }


    public void setSets(Collection<LanguageSet> sets) {
        this.sets.addAll(sets);
    }

    private void initLanguages(Collection<Language> languages) {
        languageList.setAll(languages);

        for (Language language : languageList) {
            if (language.isReference()) {
                referenceLanguage.set(language);
            }
        }
    }

    public ObservableList<Language> getLanguagesUnmodifiable() {
        return new ReadOnlyListWrapper<>(languageList);
    }

    public ObservableList<Category> getCategoriesUnmodifiable() {
        return new ReadOnlyListWrapper<>(categories);
    }


    public void addCategory(Category category) {
        checkNotNull(category);
        if (!categories.contains(category)) {
            knownKeys.addAll(category.getKeysUnmodifiable().values());
            categories.add(category);
        }
    }

    public void removeCategory(Category category) {
        checkNotNull(category);
        knownKeys.removeAll(category.getKeysUnmodifiable().values());
        categories.remove(category);
    }

    public ObservableList<Translation> createTranslations(Key key) {
        if (translationsForKey.containsKey(key)) {
            return translationsForKey.get(key);
        }

        ObservableList<Translation> translations = FXCollections.observableArrayList();

        for (Language language : languageList) {
            String translatedKey = key.getTranslation(language);
            Translation translation = new Translation(key, language, translatedKey);
            translations.add(translation);
        }

        translationsForKey.put(key, translations);
        return translations;
    }

    public ObservableList<LanguageSet> getSets() {
        return new ReadOnlyListWrapper<>(sets);
    }

    public Language getReferenceLanguage() {
        return referenceLanguage.get();
    }

    public boolean categoryExists(String categoryName) {
        checkNotNull(categoryName);
        for (Category category : categories) {
            if (categoryName.equals(category.getName())) {
                return true;
            }
        }
        return false;
    }

    public void unregisterKey(Key key) {
        knownKeys.remove(key);
    }

    public Key ensureUniqueKey(String keyName) throws TranslationException {
        for (Key key : knownKeys) {
            if (keyName.equals(key.getKey())) {
                throw new TranslationException("Key " + keyName + " is already in another category!");
            }
        }

        Key key = new Key(languageList.size(), keyName);
        knownKeys.add(key);
        return key;
    }
}
